package org.beynet.docs.sync;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

public class FileToSync extends Observable implements Observer {

    
    
    public enum Status {
        PENDING,
        IN_PROCESS,
        FAILED,
        NO_CHANGE,
        PROCESSED
    }
    
    public FileToSync(File toSync) {
        if (toSync==null) throw new IllegalArgumentException("File to sync must not be null");
        this.toSync = toSync ;
        this.status = Status.PENDING;
        this.childs = new ArrayList<FileToSync>();
    }
    
    
    @Override
    /**
     * a directory receive states of its childs
     */
    public void update(Observable o, Object o1) {
        // directories receive events of childs directories
        if (o instanceof FileToSync) {
            FileToSync f = (FileToSync)o ;
            if (Status.PROCESSED.equals(f.getStatus()) || Status.NO_CHANGE.equals(f.getStatus()) || Status.FAILED.equals(f.getStatus()))  {
                synchronized(childs) {
                    childs.remove(f);
                    if (childs.isEmpty()) {
                        this.endUpload();
                    }
                }
            }
        }
    }
    
    /**
     * @return all the files belonging to current directory - if current file is not
     * a directory an IllegalStateException will be thrown
     */
    public List<FileToSync> returnChildsList() {
        List<FileToSync> result = new ArrayList<FileToSync>();
        if (!toSync.isDirectory()) throw new IllegalStateException("current file is not a directory");
        List<File> tmp = Arrays.asList(toSync.listFiles());
        for (File f : tmp) {
            FileToSync newFileToSync = new FileToSync(f);
            newFileToSync.addObserver(this);
            childs.add(newFileToSync);
            result.add(newFileToSync);
        }
        return(result);
    }
    
    /**
     * mark that upload is starting for that file
     */
    public void startUpload() {
        status = Status.IN_PROCESS;
        setChanged();
        notifyObservers();
    }
    /**
     * this file will not be synced because remote side an local side are equals
     */
    public void noChange() {
        status = Status.NO_CHANGE;
        setChanged();
        notifyObservers();
    }
    
    /**
     * mark this file as correctly uploaded
     */
    public void endUpload(){
        status = Status.PROCESSED;
        setChanged();
        notifyObservers();
    }
    
    /**
     * an error occured during upload
     */
    public void uploadFailed() {
        status=Status.FAILED;
        setChanged();
        notifyObservers();
    }
    
    /**
     * receive an upload rate
     * @param alreadySent 
     */
    public void uploadRate(long alreadySent) {
        Map<String,Long> rate = new HashMap<String, Long>();
        rate.put(FILE_SENT, Long.valueOf(alreadySent));
        rate.put(FILE_SIZE, Long.valueOf(toSync.length()));
        setChanged();
        notifyObservers(rate);
    }
    
    /**
     * @return the file to be synced
     */
    public File getFile() {
        return(toSync);
    }
    
    /**
     * @return current status of the sync
     */
    public Status getStatus() {
        return(this.status);
    }
    
    private File         toSync   ;
    private Status       status   ;
    private final List<FileToSync> childs   ;
    
    public final static String FILE_SIZE ="fileSize";
    public final static String FILE_SENT ="fileSent";
}
